package com.zeroes.cons;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.List;

import org.zeroes.Components.ZSSingle;
import org.zeroes.Events.ZSPair;
import org.zeroes.common_prelude.*;
import org.zeroes.svg.*;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

public class ZSSprite {

	// transform values
	public vector2 position = new vector2(0,0);
	public float rotation,p_rotation;
	public float scaleX=1;
	public float scaleY=1;
	public float p_sx=1;
	public float p_sy=1;
	public float px=0;
	public float py=0;
	private float fx=1.0f;
	private float fy=1.0f;
	private SVGElement el=null;
	public float opacity=1.0f;
	public String tag="";
	public String parentID="freeEntities";
	public float anchorX=0.5f;
	public float anchorY=0.5f;
	private Quad tq=new Quad();
	private Quad _tq=new Quad();
	private int ZOrder=0;
	protected boolean ouv=false;
	private boolean batched=false;
	private int srcBlend;
	private int destBlend;
	public boolean visible=true;
	protected float[] MVPMatrix = new float[16];
	private float[] MVMatrix = new float[16];
	private ZSGroup group;
	protected ZSTexture2D _texture;
	private FloatBuffer texCoords;
    public float[] getTexCoordsArray() {
    	float ret[] = new float[texCoords.limit()];
    	texCoords.get(ret, 0, texCoords.limit());
    	return ret;
    }
    
    private FloatBuffer vertices;
    protected FloatBuffer _verts;
    public float[] getVertexArray() {
    	float ret[] = new float[_verts.limit()];
    	_verts.get(ret, 0, _verts.limit());
    	return ret;
    }
    
    protected ShortBuffer indices;
    
    public FloatBuffer getTexCoords() {
    	texCoords.position(0);
    	return texCoords;
    }
    
    public FloatBuffer getVertices() {
    	vertices.position(0);
    	return vertices;
    }
    private List<ZSPair<String,?>> _data=new ArrayList<ZSPair<String,?>>();
    protected Quad quad;
    private FloatBuffer colors;
    
    public Rect rect;
    protected ZSRenderer _gl=ZSGameManager.AskMe().GL;
    protected boolean transformDirty;
    protected boolean transformGroupDirty=true;
    public ZSSprite(){
		if(ZSGameManager.AskMe().forCurrentGame().gameOptions.autoAdjustToScreenSize){
		fx=ZSGameManager.AskMe().forCurrentGame().getXRatio();
		fy=ZSGameManager.AskMe().forCurrentGame().getYRatio();
		}
    	this.setScale(fx, fy);
    }
    public ZSSprite(ZSTexture2D texture){
		if(ZSGameManager.AskMe().forCurrentGame().gameOptions.autoAdjustToScreenSize){
		fx=ZSGameManager.AskMe().forCurrentGame().getXRatio();
		fy=ZSGameManager.AskMe().forCurrentGame().getYRatio();
		}
    	Rect r = new Rect(0,0,texture.width(),texture.height());
    	rect =r;
    	
    	init(texture, r);
    	this.setScale(fx, fy);
    	this.marktransformDirty();
    	//transformDirty=true;
    	
    	Matrix.setIdentityM(MVPMatrix, 0);
    }
    
    public ZSSprite(int resID){
    	Context ctx = ZSGameManager.AskMe().ctx;
		if(ZSGameManager.AskMe().forCurrentGame().gameOptions.autoAdjustToScreenSize){
		fx=ZSGameManager.AskMe().forCurrentGame().getXRatio();
		fy=ZSGameManager.AskMe().forCurrentGame().getYRatio();
		}
    	ZSTexture2D tex = new ZSTexture2D(ctx.getResources().openRawResource(resID));
    	Rect r = new Rect(0,0,tex.width(),tex.height());
    	rect =r;
    	init(tex, r);
    	//transformDirty=true;
    	this.setScale(fx, fy);
    	this.marktransformDirty();
    	Matrix.setIdentityM(MVPMatrix, 0);

    }
    
    public ZSSprite(String id, Rect rect){
		if(ZSGameManager.AskMe().forCurrentGame().gameOptions.autoAdjustToScreenSize){
		fx=ZSGameManager.AskMe().forCurrentGame().getXRatio();
		fy=ZSGameManager.AskMe().forCurrentGame().getYRatio();
		}
    	this.tag=id;
    	this.rect=new Rect(0,0,rect.width(),rect.height(),rect.id);
    	this.batched=true;
    	quad=new Quad();
    	quad.set_from_rect(this.rect);
    	_texture=null;
    	this.parseCustomData();
    	this.setScale(fx, fy);
    	this.marktransformDirty();
    }
    public void changeRect(Rect newRect){
    	
    	this.rect=new Rect(0,0,newRect.width(),newRect.height(),newRect.id);
    	
    	quad=new Quad();
    	quad.set_from_rect(this.rect);
     	this.marktransformDirty(); 	
    }
    
	public ZSSprite(String id){
		//Log.v("gtrans", "sprite constr");
		ZSTexture2D tex=null;
		boolean valid=false;
		Context ctx = ZSGameManager.AskMe().ctx;

		if(ZSGameManager.AskMe().forCurrentGame().gameOptions.autoAdjustToScreenSize){
		fx=ZSGameManager.AskMe().forCurrentGame().getXRatio();
		fy=ZSGameManager.AskMe().forCurrentGame().getYRatio();
		}

		SVGElement node = ZSGameManager.AskMe().gameDOM().getNodeById(id);
		if(!node.equals(null)){
		 tex= new ZSTexture2D(node.imageBytes);
		 valid=true;
		}
		else
		{
			tex= new ZSTexture2D(ctx.getResources().openRawResource(R.raw.zssigla));
		}
		setuptexture(tex);
		if(valid)
		{
			this.tag=id;
			this.setPosition(node.x*fx+rect.width()/2-(1-fx)*rect.width()/2,node.y*fy+rect.height()/2-(1-fy)*rect.height()/2);
			this.setRotation(node.rot);
			// aici am modificat peantru a testa scalarea automata  (node.sx , node.sy)

			this.setScale(node.sx*fx, node.sy*fy);
			/*if(tag.equals("bcg"))
				Log.v("gtrans","bcg width rect "+rect.width());*/
			this.opacity=node.opacity;
			this.ZOrder=node.z;
		}
		this.parseCustomData();
		
	}
	
	public void newRect(Rect nr){
		
		this.rect=new Rect(0,0,nr.width(),nr.height(),nr.id);
    	quad.set_from_rect(this.rect);
    	
    	this.marktransformDirty();
	}
	private void setuptexture(ZSTexture2D t){
		Rect r = new Rect(0,0,t.width(),t.height());
    	rect =r;
    	init(t, r);
    	//transformDirty=true;
    	this.marktransformDirty();
    	Matrix.setIdentityM(MVPMatrix, 0);
		
	}
    protected void init(ZSTexture2D tex, Rect r){
      
    	int w=_gl.mWidth,h=_gl.mHeight;
    	float hw=w/2,hh=h/2;
    	_texture = tex;
		this.srcBlend = GLES20.GL_ONE;
		this.destBlend = GLES20.GL_ONE_MINUS_SRC_ALPHA;
       /* float[] mVertsData1 =
            { 
                    -400.0f, 240.0f, 0.0f, // Position 0
                    0.0f, 0.0f, // TexCoord 0
                    -400.0f, -16.0f, 0.0f, // Position 1
                    0.0f, 1.0f, // TexCoord 1
                    -144.0f,-16.0f, 0.0f, // Position 2
                    1.0f, 1.0f, // TexCoord 2
                    -144.0f, 240.0f, 0.0f, // Position 3
                    1.0f, 0.0f // TexCoord 3
            };*/
    	quad=new Quad();
    	quad.set_from_rect(r);
    	
    
    	float[] mVertsData1 =
            { 
                    -hw, hh, 0.0f, // Position 0
                    0.0f, 0.0f, // TexCoord 0
                    -hw, hh-r.height(), 0.0f, // Position 1
                    0.0f, 1.0f, // TexCoord 1
                    -hw+r.width(),hh-r.height(), 0.0f, // Position 2
                    1.0f, 1.0f, // TexCoord 2
                    -hw+r.width(), hh, 0.0f, // Position 3
                    1.0f, 0.0f // TexCoord 3
            };
        
        _verts = ByteBuffer.allocateDirect(mVertsData1.length * 4)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        _verts.put(mVertsData1).position(0);
        
        
        short[] mIndicesData =
            { 
                    0, 1, 2, 0, 2, 3 
            };
        
        indices = ByteBuffer.allocateDirect(mIndicesData.length * 2)
                .order(ByteOrder.nativeOrder()).asShortBuffer();
        indices.put(mIndicesData).position(0);
    	
    }
    public void visitLocal(float a){
    	
    	transform(a);
    	//if(this.group.id.equals("freeEntities"))
    	if(!batched)
    	draw();
    	
    	transformDirty=false;
    }
    
    public void visitGrouped(float alpha){

    	float rotation=this.rotation ;
    	float sx=this.scaleX,sy=this.scaleY;
    	float px=this.position.x,py=this.position.y;

		if (transformGroupDirty){
			/*if(alpha!=0){

			rotation = (this.rotation * alpha) + (this.p_rotation * (1f - alpha));
			sx = (this.scaleX * alpha) + (this.p_sx * (1f - alpha));
			sy = (this.scaleY * alpha) + (this.p_sy * (1f - alpha));
			px = (this.position.x * alpha) + (this.px * (1f - alpha));
			py = (this.position.y * alpha) + (this.py * (1f - alpha));
			}
			//Log.d("ConstructoidActivity", "rotation "+this.rotation+" ,p_rotation "+this.p_rotation+" alpha "+alpha );
			*/
			
			tq.setfromQuad(quad).offset((int)(px-anchorX*rect.width()), (int)(py-anchorY*rect.height()));
			//tq.rotateZ(rotation,position.x, position.y);
		//Log.v("gtrans", this.tag+" transformed (grouped)"+this.group.gcx+" , "+this.group.gcy);
		
			//	tq.rotateZ(this.group.rotation,this.group.gcx,this.group.gcy);

		//	tq.scale(sx,sy,position.x, position.y);
			//this.updateVertsfromQuad(tq);
		}
		this.transformGroupDirty=false;
		//draw();
    }
    public Quad getTransformedQuad(){
    	
    	return this.tq;
    }
	private void transform(float alpha){
		if(!visible)
			 return;
		
    	int w=_gl.mWidth,h=_gl.mHeight;
    	float hw=w/2,hh=h/2;
    	float rotation=this.rotation ;
    	float sx=this.scaleX,sy=this.scaleY;
    	float px=this.position.x,py=this.position.y;
    	//Quad tq = new Quad();
		if (transformDirty){
			if(alpha!=0){

			rotation = (this.rotation * alpha) + (this.p_rotation * (1f - alpha));
			sx = (this.scaleX * alpha) + (this.p_sx * (1f - alpha));
			sy = (this.scaleY * alpha) + (this.p_sy * (1f - alpha));
			px = (this.position.x * alpha) + (this.px * (1f - alpha));
			py = (this.position.y * alpha) + (this.py * (1f - alpha));
			}
			//Log.d("ConstructoidActivity", "rotation "+this.rotation+" ,p_rotation "+this.p_rotation+" alpha "+alpha );

			
			//tq.setfromQuad(quad).offset((int)(position.x-anchorX*rect.width()), (int)(position.y-anchorY*rect.height()));
			tq.setfromQuad(quad).offset((group.layer.tx+group.tx+px-anchorX*rect.width()), (group.layer.ty+group.ty+py-anchorY*rect.height()));
			//	float arX=tq.tlx+anchorX*(tq.trx-tq.tlx);
			//float arY=tq.tlx+anchorX*(tq.trx-tq.tlx);
			_tq.setfromQuad(tq);
			tq.rotateZ(rotation,position.x, position.y,group);
			
			
			if(position.x-anchorX*rect.width()>_gl.mWidth||position.x+anchorX*rect.width()<0||position.y-anchorY*rect.height()>_gl.mHeight||position.y+anchorY*rect.height()<0)
				this.ouv=true;
			else
				this.ouv=false;
			//tq.scale(scaleX,scaleY,position.x, position.y);
			tq.scale(sx,sy,position.x, position.y,group);
		
			//_tq=tq;
			if(!batched)
			this.updateVertsfromQuad(tq);
			

			
		}
	}
	
	public void savepreupdateValues(){
		p_rotation=rotation;
		p_sx=scaleX;
		p_sy=scaleY;
		px=this.position.x;
		py=this.position.y;
	}
	public int getZ(){
		
		return this.ZOrder;
	}
	public void rotateClockwise(float deg){
		this.rotation+=deg;
		
		if(this.rotation>=360)
		{
			this.rotation-=360;
			this.p_rotation-=360;
		}
		this.marktransformDirty();
		
	}
	
	public void setPosition(float x,float y){
		this.position.x=x;
		this.position.y=y;
		this.marktransformDirty();
		
	}
	
	public void $Position(float x,float y){
		this.position.x=x*fx;
		this.position.y=y*fy;
		this.marktransformDirty();
		
	}
	
	public void setScale(float onX,float onY){
		this.scaleX=onX;
		this.scaleY=onY;
		this.marktransformDirty();
	}
	public void setRotation(float deg){
		if(deg>360)
		this.rotation=deg-360;
		else
			this.rotation=deg;
		
		this.marktransformDirty();
	}
	public Rect getBBox(){
		
		return this._tq.bounding_box();
	}
	
	public void rotateCounterClockwise(float deg){
		this.rotation-=deg;
		
		if(this.rotation<=-360)
		{
			this.rotation+=360;
			this.p_rotation+=360;
		}
		this.marktransformDirty();
		
	}
	protected void updateVertsfromQuad(Quad q){
    	int w=_gl.mWidth,h=_gl.mHeight;
    	float hw=w/2,hh=h/2;
    

		float[] mVertsData1 =
            { 
                    -hw+q.tlx, hh-q.tly, 0.0f, // Position 0
                    0.0f, 0.0f, // TexCoord 0
                    -hw+q.blx, hh-q.bly, 0.0f, // Position 1
                    0.0f, 1.0f, // TexCoord 1
                    -hw+q.brx,hh-q.bry, 0.0f, // Position 2
                    1.0f, 1.0f, // TexCoord 2
                    -hw+q.trx, hh-q.try_, 0.0f, // Position 3
                    1.0f, 0.0f // TexCoord 3
            };
        _verts.position(0);
        _verts.put(mVertsData1).position(0);
    	

        
	}
	public void setBatched(boolean b){
		this.batched=b;
		
	}
	public void marktransformDirty(){
		transformDirty=true;
		if(this.group!=null)
			this.group.transformDirty=true;
	}
	
	
	protected void draw(){
		if(!visible||ouv)
		 return;
		blend();
		int prog = _gl.mProgramObject;
		GLES20.glUseProgram(prog);
		
		 int PositionLoc = GLES20.glGetAttribLocation(prog, "aPosition");
	     int TexCoordLoc = GLES20.glGetAttribLocation(prog, "aTexCoord" );
	     int sLoc = GLES20.glGetUniformLocation ( prog, "sTexture" );
	     
	        _verts.position(0);	     
	        GLES20.glVertexAttribPointer ( PositionLoc, 3, GLES20.GL_FLOAT, 
                    false, 
                    5 * 4, _verts );
	        _verts.position(3);
	        GLES20.glVertexAttribPointer ( TexCoordLoc, 2, GLES20.GL_FLOAT,
                    false, 
                    5 * 4, 
                    _verts );

	        GLES20.glEnableVertexAttribArray ( PositionLoc );
	        GLES20.glEnableVertexAttribArray ( TexCoordLoc );
	        
	        // Bind the base map
	        GLES20.glActiveTexture ( GLES20.GL_TEXTURE0 );
	        GLES20.glBindTexture ( GLES20.GL_TEXTURE_2D, _texture.texID() );

	        // Set the base map sampler to texture unit to 0
	        GLES20.glUniform1i ( sLoc, 0 );
	        GLES20.glUniform1f(GLES20.glGetUniformLocation(prog, "opacity"), opacity);
	        //Matrix.multiplyMM(MVPMatrix, 0, _gl.mPMatrix.data, 0, MVMatrix, 0);
	        
	        Matrix.multiplyMM(MVPMatrix, 0, _gl.matP.data, 0, _gl.matMV.data, 0);
	        GLES20.glUniformMatrix4fv(GLES20.glGetUniformLocation(prog, "matP"), 1, false, _gl.matP.data, 0);
	        GLES20.glUniformMatrix4fv(GLES20.glGetUniformLocation(prog, "matMV"), 1, false, _gl.matMV.data, 0);
	        //GLES20.glUniformMatrix4fv(GLES20.glGetUniformLocation(prog, "uMVPMatrix"), 1, false, MVPMatrix, 0);
	        GLES20.glDrawElements ( GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, indices );    
	     /* if(this.tag.equals("text"))
	          Log.v("gtrans", "draw from sprite text .... ");*/

	}
	public void newTexture(ZSTexture2D tex){
		this._texture=tex;
		this.setuptexture(tex);
		
	}
	public void onTouch(){
	/*	float ltx = TouchCentral.AskMe().lastTouch.lastX;
		float lty = TouchCentral.AskMe().lastTouch.lastY;
		int m = TouchCentral.AskMe().lastTouch.mode;
	
		position.x=(int)ltx;
		position.y=(int)lty;
		this.marktransformDirty();*/
	}
	
	public void setZOrder(int newOrder){
		
		this.ZOrder=newOrder;
	}
	public void setparentID(String nID){
		
		this.parentID=nID;
	}
	
	public void setGroup(ZSGroup g){
		
		this.group=g;
	}
	public ZSGroup Group(){
		
		return group;
	}
	public ZSLayer Layer(){
		
		return group.layer;
	}
	
	public void marktransformGroupDirty(){
		
		this.transformGroupDirty=true;
	}
	public void setOpacity(float o){
		if(o>1){
			this.opacity=1;
		}
		else{
			if(o<0) 
				this.opacity=0;
			else
				this.opacity=o;
		}
		
	}
	public void hide(){
		this.visible=false;		
		
	}
	public void show(){
		this.visible=true;
	}
	
	public boolean containPoint(vector2 p){
		
		 return this.tq.contains_pt(p.x, p.y);
			
			
		
	}
	public void data(String tag, ZSSingle<?> d){
		String[] t = tag.split(":");
		if(t[0].equals("S")){
			this._data.add(new ZSPair<String,String>(tag,(String)d.getFirst()));
		}
		if(t[0].equals("I")){
			this._data.add(new ZSPair<String,Integer>(tag,(Integer)d.getFirst()));
		}
		if(t[0].equals("F")){
			this._data.add(new ZSPair<String,Float>(tag,(Float)d.getFirst()));
		}
		if(t[0].equals("B")){
			this._data.add(new ZSPair<String,Boolean>(tag,(Boolean)d.getFirst()));
		}
		
	}
	public ZSSingle<?> data(String tag){
		for(ZSPair<?,?> p : this._data){
			String _t = (String)p.getFirst();
			String[] t = tag.split(":");
			if(_t.equals(tag)){
				if(t[0].equals("S"))
				return new ZSSingle<String>((String)p.getSecond());
				if(t[0].equals("I"))
				return new ZSSingle<Integer>((Integer)p.getSecond());
				if(t[0].equals("F"))
				return new ZSSingle<Float>((Float)p.getSecond());
				if(t[0].equals("B"))
				return new ZSSingle<Boolean>((Boolean)p.getSecond());
			}
		}
		return null;
	}
	public String Sdata(String untypedTag){
		if(this._data.size()==0)
			this.parseCustomData();
		return (String)this.data("S:"+untypedTag).getFirst();
	}
	public Integer Idata(String untypedTag){
		if(this._data.size()==0)
			this.parseCustomData();
		return (Integer)this.data("I:"+untypedTag).getFirst();
	}
	public float Fdata(String untypedTag){
		if(this._data.size()==0)
			this.parseCustomData();
		return (Float)this.data("F:"+untypedTag).getFirst();
	}
	public boolean Bdata(String untypedTag){
		if(this._data.size()==0)
			this.parseCustomData();
		return (Boolean)this.data("B:"+untypedTag).getFirst();
	}
	public void data(){
		this._data.clear();
		
	}
	public void setElement(SVGElement e){
		this.el=e;
		
	}
	private void parseCustomData(){
		
		if(this.el!=null){
			//Log.v("gtrans", "Parse data ... "+this.tag);
			if(this.el.getCustomData()!=null){
				
				// todo ....
				
				String[] items = this.el.getCustomData().split(",");
				for(int i=0;i<items.length;i++){
					
					String[] eachp = items[i].split(":");
					if(eachp[0].startsWith("I")){
						
						this.data("I:"+eachp[0].substring(1), new ZSSingle<Integer>(Integer.valueOf(eachp[1]).intValue()));
					}
					if(eachp[0].startsWith("S")){
						
						this.data("S:"+eachp[0].substring(1), new ZSSingle<String>(eachp[1]));
					}
					if(eachp[0].startsWith("F")){
						this.data("F:"+eachp[0].substring(1), new ZSSingle<Float>(Float.valueOf(eachp[1]).floatValue()));
						
					}
					if(eachp[0].startsWith("B")){
						if(eachp[1].equals("true"))
						this.data("B:"+eachp[0].substring(1), new ZSSingle<Boolean>(true));
						else
							this.data("B:"+eachp[0].substring(1), new ZSSingle<Boolean>(false));
					}
				}
			}
		}
	}
	
	public int getTextureID(){
		if(this._texture!=null)
		return this._texture.texID();
return -1;
	}
	
public void reloadTexture(){
	if(this._texture!=null)
	this._texture.reload();
}

public float getFx(){
	return this.fx;
}
public float getFy(){
	return this.fy;
}
 private void blend(){
	 GLES20.glBlendFunc(this.srcBlend,this.destBlend);
 }
 
 public void updateBlend(int effect){
	 switch (effect) {
		case 1:		// lighter (additive)
			this.srcBlend = GLES20.GL_ONE;
			this.destBlend = GLES20.GL_ONE;
			break;
		case 2:		// xor
			break;	// todo
		case 3:		// copy
			this.srcBlend = GLES20.GL_ONE;
			this.destBlend = GLES20.GL_ZERO;
			break;
		case 4:		// destination-over
			this.srcBlend = GLES20.GL_ONE_MINUS_DST_ALPHA;
			this.destBlend = GLES20.GL_ONE;
			break;
		case 5:		// source-in
			this.srcBlend = GLES20.GL_DST_ALPHA;
			this.destBlend = GLES20.GL_ZERO;
			break;
		case 6:		// destination-in
			this.srcBlend = GLES20.GL_ZERO;
			this.destBlend = GLES20.GL_SRC_ALPHA;
			break;
		case 7:		// source-out
			this.srcBlend =GLES20.GL_ONE_MINUS_DST_ALPHA;
			this.destBlend = GLES20.GL_ZERO;
			break;
		case 8:		// destination-out
			this.srcBlend = GLES20.GL_ZERO;
			this.destBlend = GLES20.GL_ONE_MINUS_SRC_ALPHA;
			break;
		case 9:		// source-atop
			this.srcBlend = GLES20.GL_DST_ALPHA;
			this.destBlend = GLES20.GL_ONE_MINUS_SRC_ALPHA;
			break;
		case 10:	// destination-atop
			this.srcBlend = GLES20.GL_ONE_MINUS_DST_ALPHA;
			this.destBlend = GLES20.GL_SRC_ALPHA;
			break;
		}	
	 
 }
 
public void unloadTexture(){
	int[] ts = new int[1];
	ts[0]=this.getTextureID();
	
	if(ts[0]>-1){
		Log.v("gtrans", "Unload texture id : "+ts[0]);
		GLES20.glDeleteTextures(1, ts, 0);
	}
}

}
